package org.traffometer.traffosim;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.FileFilter;
import java.util.List;

import org.junit.Test;
import org.traffometer.common.data.TraffoPacket;

/**
 * Tests the Config class.
 * 
 * @author Robert S. Brewer
 */
public class TestConfig {
  
  /**
   * Filename of known good configuration file, including relative path.  
   */
  protected static final String GOOD_CONFIG_FILENAME = "lib/config/static-sample.cfg";

  /**
   * Filename prefix of known bad configuration files, including relative path.  
   */
  protected static final String BAD_CONFIG_FILENAME_PREFIX = "lib/config/test-cases/";

  /**
   * Short filter that looks for our bad test files.
   * 
   * @author Robert S. Brewer
   */
  private static class TestCaseFilter implements FileFilter {
    /**
     * Tests whether a file meets the criteria: normal, readable files whose names start with
     * "bad-".
     * 
     * @param file File to be tested against criteria.
     * @return true if meets criteria, false otherwise.
     */
    public boolean accept(File file) {
      return file.isFile() && file.canRead() && file.getName().startsWith("bad-");
    }
  }
  
  /**
   * Tests building a configuration from configuration files that have syntax or content
   * errors. Each one should fail by throwing a ConfigException, failure to throw one or
   * throwing a RuntimeException of any kind is a failure.
   */
  @Test
  public void testBadConfigs() {
    Config config;
    File testDirectory = new File(BAD_CONFIG_FILENAME_PREFIX);
    File[] files;
    
    files = testDirectory.listFiles(new TestCaseFilter());
    // We now have a list of all the bad test cases to run
    for (int i = 0; i < files.length; i++) {
      try {
        config = new Config(files[i]);
        fail("bad configuration file was loaded without throwing ConfigException. Filename: " +
            files[i].getName() + "\nConfig: " + config);
      }
      catch (ConfigException e) {
        assert true : "Expected behavior, bad config -> ConfigException";
      }
      catch (RuntimeException e) {
        fail("bad configuration file threw a RuntimeException while loading. Filename: " +
            files[i].getName() + "\nException: " + e);
      }
    }
  }

  /**
   * Tests getPackets() method of Config. Currently it just checks that there were 10
   * packets received.
   */
  @Test
  public void testGetPackets() {
    Config config = loadGoodConfig();
    
    List<TraffoPacket> packets = config.getPackets();
    assertEquals("incorrect number of packets found in file", 10, packets.size());
  }
  
  /**
   * Tests the toString method, but doesn't actually parse the output since that will
   * change when TraffoPacket is fully implemented. 
   */
  @Test
  public void testToString() {
    Config config = loadGoodConfig();
    String output;
    try {
      output = config.toString();
      assertNotNull("toString returned null value", output);
      if ("".equals(output)) {
        fail("toString returned empty String");
      }
    }
    catch (RuntimeException e) {
      fail("encountered exception running toString: " + e);
    }
  }
  
  /**
   * Creates a Config object by loading in the canonical static test file.
   * 
   * @return new Config object loaded from canonical test file. If config file can't
   * be loaded, failure will be asserted (i.e. should be no need to check for null).
   */
  public static Config loadGoodConfig() {
    Config config = null;
    
    try {
      config = new Config(new File("lib/config/static-sample.cfg"));
    }
    catch (ConfigException e) {
      fail("Unable to load sample configuration file: " + e.toString());
    }
    // Added to placate compiler, should never be reachable.
    return config;
  }
}
